package com.example.demo.util;


import com.example.demo.config.FileSystemConfig;
import com.example.demo.config.MinioConfig;
import io.minio.*;
import io.minio.messages.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedList;
import java.util.List;

/**
 * Minio工具类
 *
 * @author yz
 */
@Slf4j
@Component
public class MinioUtil {

    private MinioClient minioClient;

    @Resource
    private FileSystemConfig fileSystemConfig;

    @Resource
    private MinioConfig minioConfig;

    private String bucketName;
    private List<String> bucketNameList = new ArrayList<>();

    @PostConstruct
    private void connectToServer() {
        if (fileSystemConfig.isMinioEnable()) {
            // 初始化 MinioClient
            minioClient = MinioClient.builder()
                    .endpoint(minioConfig.getUrl())
                    .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                    .build();
            // 检测默认 bucket 是否存在
            bucketName = minioConfig.getBucketName();
            bucketExists(bucketName);
            // 设置默认 bucket 的存储策略（超期自动删除）
            setBucketLifecycleByName(bucketName);
        }
    }

    /**
     * 根据 bucketName 检测是否存在 不存在则创建
     *
     * @param bucketName 桶名称
     */
    public void bucketExists(String bucketName) {
        try {
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!found) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                bucketNameList.add(bucketName);
            }
        } catch (Exception e) {
            log.error("bucketExists Failed!");
            e.printStackTrace();
        }
    }

    /**
     * 查询所有桶数据
     */
    public void bucketList() {
        try {
            List<Bucket> bucketList = minioClient.listBuckets();
            for (Bucket bucket : bucketList) {
                bucketNameList.add(bucket.name());
            }
        } catch (Exception e) {
            log.error("bucketList Failed!");
            e.printStackTrace();
        }
    }

    /**
     * 根据bucket名称删除对应的桶
     *
     * @param bucketName 桶名称
     */
    public void bucketRemove(String bucketName) {
        try {
            RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build();
            minioClient.removeBucket(removeBucketArgs);
        } catch (Exception e) {
            log.error("bucketList Failed!");
            e.printStackTrace();
        }


    }

    /**
     * 根据对象名称和文件路径上传文件
     *
     * @param objectName 对象名称
     * @param filePath   文件路径
     */
    public void fileUploadByFilePath(String objectName, String filePath) {
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .filename(filePath)
                    .build();
            minioClient.uploadObject(uploadObjectArgs);
        } catch (Exception e) {
            log.error("fileUploader [{}] Failed!", filePath);
            e.printStackTrace();
        }
    }

    /**
     * 根据桶和对象名称和文件路径上传文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param filePath   文件路径
     */
    public void fileUploadByFilePath(String bucketName, String objectName, String filePath) {
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .filename(filePath)
                    .build();
            minioClient.uploadObject(uploadObjectArgs);
        } catch (Exception e) {
            log.error("fileUploader [{}] Failed!", filePath);
            e.printStackTrace();
        }
    }

    /**
     * （图片上传）根据对象名称和base64数据上传文件
     *
     * @param objectName 对象名称
     * @param imageData  图片base64数据
     */
    public void fileUploadByImageData(String objectName, String imageData) {
        try {
            byte[] imageBytes = Base64.getDecoder().decode(imageData);
            InputStream inputStream = new ByteArrayInputStream(imageBytes);
            // 上传配置
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .stream(inputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .contentType("image/png")
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            log.error("fileUploadByImageData [{}] Failed!", objectName);
            e.printStackTrace();
        }
    }

    /**
     * （图片上传）根据桶和对象名称和base64数据上传文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @param imageData  图片base64数据
     */
    public void fileUploadByImageData(String bucketName, String objectName, String imageData) {
        try {
            byte[] imageBytes = Base64.getDecoder().decode(imageData);
            InputStream inputStream = new ByteArrayInputStream(imageBytes);
            // 上传配置
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .stream(inputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .contentType("image/png")
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            log.error("fileUploadByImageData [{}] Failed!", objectName);
            e.printStackTrace();
        }
    }

    /**
     * 根据对象名称和目标路径下载文件
     *
     * @param objectName     对象名称
     * @param targetFilePath 目标路径
     */
    public void fileDownload(String objectName, String targetFilePath) {
        Path path = new File(targetFilePath).toPath();
        try {
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            InputStream stream = minioClient.getObject(getObjectArgs);
            Files.copy(stream, path, StandardCopyOption.REPLACE_EXISTING);
        } catch (Exception e) {
            log.error("fileDownload [{}] Failed!", targetFilePath);
            e.printStackTrace();
        }

    }

    /**
     * 根据桶和对象名称和目标路径下载文件
     *
     * @param bucketName     桶名称
     * @param objectName     对象名称
     * @param targetFilePath 目标路径
     */
    public void fileDownload(String bucketName, String objectName, String targetFilePath) {
        Path path = new File(targetFilePath).toPath();
        try {
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            InputStream stream = minioClient.getObject(getObjectArgs);
            Files.copy(stream, path, StandardCopyOption.REPLACE_EXISTING);
        } catch (Exception e) {
            log.error("fileDownload [{}] Failed!", targetFilePath);
            e.printStackTrace();
        }

    }

    /**
     * 根据对象名称删除文件
     *
     * @param objectName 对象名称
     */
    public void fileDelete(String objectName) {
        try {
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("fileDelete [{}] Failed!", objectName);
            e.printStackTrace();
        }
    }

    /**
     * 根据桶和对象名称删除文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     */
    public void fileDelete(String bucketName, String objectName) {
        try {
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("fileDelete [{}] Failed!", objectName);
            e.printStackTrace();
        }
    }

    /**
     * 根据对象名称预览文件
     *
     * @param objectName 对象名称
     * @return 对象流
     */
    public InputStream filePreview(String objectName) {
        InputStream stream = null;
        try {
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName).build();
            stream = minioClient.getObject(getObjectArgs);
        } catch (Exception e) {
            log.error("fileDelete [{}] Failed!", objectName);
            e.printStackTrace();
        }
        return stream;
    }

    /**
     * 根据桶和对象名称预览文件
     *
     * @param bucketName 桶名称
     * @param objectName 对象名称
     * @return 对象流
     */
    public InputStream filePreview(String bucketName, String objectName) {
        InputStream stream = null;
        try {
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName).build();
            stream = minioClient.getObject(getObjectArgs);
        } catch (Exception e) {
            log.error("fileDelete [{}] Failed!", objectName);
            e.printStackTrace();
        }
        return stream;
    }

    /**
     * 根据桶和对象名称预览文件
     *
     * @param bucketName 桶名称
     * @return 对象流
     */
    public void setBucketLifecycleByName(String bucketName) {
        try {
            List<LifecycleRule> rules = new LinkedList<>();
            // 设置过期时间
            rules.add(
                    new LifecycleRule(
                            Status.ENABLED,
                            null,
                            new Expiration((ZonedDateTime) null, minioConfig.getObjectKeepDays(), null),
                            new RuleFilter(""),
                            "rule1",
                            null,
                            null,
                            null));
            LifecycleConfiguration config = new LifecycleConfiguration(rules);
            minioClient.setBucketLifecycle(
                    SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build());
        } catch (Exception e) {
            log.error("setBucketLifecycleByName [{}] Failed!", bucketName);
            e.printStackTrace();
        }
    }

}
